package com.wxy.demo.util;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPOutputStream;

@Slf4j
public class CompressUtil {

    private static final int BUFFER_SIZE = 1024 * 100;

    public static void main(String[] args) throws Exception {
        int size = 1024 * 1024 * 10;
        FileUtil.appendString("a".repeat(size), "d:\\a.txt", StandardCharsets.UTF_8);

        File file = new File("d:\\a.txt");
        CompressUtil.compressTar(Arrays.asList(file), "d:/tmp/test/", "test.tar.gz");

//        File tarFile = new File("d:/tmp/test/test.tar.gz");
//        CompressUtil.decompressTarGz(tarFile, "d:/tmp/test/test");
    }

    public static File compressTar(List<File> list, String outPutPath, String fileName) throws Exception {
        File file = new File(outPutPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        File outPutFile = new File(outPutPath + File.separator + fileName);
        File tempTar = new File("temp.tar");
        try (FileInputStream fis = new FileInputStream(pack(list, tempTar))) {
            try (BufferedInputStream bis = new BufferedInputStream(fis, BUFFER_SIZE)) {
                try (FileOutputStream fos = new FileOutputStream(outPutFile)) {
                    try (GZIPOutputStream gzp = new GZIPOutputStream(fos)) {
                        int count;
                        byte[] data = new byte[BUFFER_SIZE];
                        while ((count = bis.read(data, 0, BUFFER_SIZE)) != -1) {
                            gzp.write(data, 0, count);
                        }
                    }
                }
            }
        }
        try {
            Files.deleteIfExists(tempTar.toPath());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return outPutFile;
    }

    /**
     * 私有函数将文件集合压缩成tar包后返回
     *
     * @param files  要压缩的文件集合
     * @param target tar 输出流的目标文件
     * @return File  指定返回的目标文件
     */
    public static File pack(List<File> files, File target) throws IOException {
        try (FileOutputStream out = new FileOutputStream(target)) {
            try (BufferedOutputStream bos = new BufferedOutputStream(out, BUFFER_SIZE)) {
                TarArchiveOutputStream os = new TarArchiveOutputStream(bos);
                try {
                    //解决文件名过长问题
                    os.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
                    for (File file : files) {
                        //去掉文件前面的目录
                        os.putArchiveEntry(new TarArchiveEntry(file, FilenameUtils.getName(file.getAbsolutePath())));
                        try (FileInputStream fis = new FileInputStream(file)) {
                            IOUtils.copy(fis, os);
                            os.closeArchiveEntry();
                        }
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                } finally {
                    os.close();
                }
            }
        }
        return target;
    }

    public static void decompressTarGz(File file, String outputDir) throws IOException {
        try (TarArchiveInputStream tarIn = new TarArchiveInputStream(
                new GzipCompressorInputStream(
                        new BufferedInputStream(
                                new FileInputStream(file))))) {
            //创建输出目录
            createDirectory(outputDir, null);
            TarArchiveEntry entry = null;
            while ((entry = tarIn.getNextTarEntry()) != null) {
                //是目录
                if (entry.isDirectory()) {
                    //创建空目录
                    createDirectory(outputDir, entry.getName());
                } else {
                    //是文件
                    try (OutputStream out = new FileOutputStream(
                            new File(outputDir + File.separator + entry.getName()))) {
                        writeFile(tarIn, out);
                    }
                }
            }
        }
    }

    public static void createDirectory(String outputDir, String subDir) {
        File file = new File(outputDir);
        //子目录不为空
        if (!(subDir == null || subDir.trim().equals(""))) {
            file = new File(outputDir + File.separator + subDir);
        }
        if (!file.exists()) {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            file.mkdirs();
        }
    }

    public static void writeFile(InputStream in, OutputStream out) throws IOException {
        int length;
        byte[] b = new byte[BUFFER_SIZE];
        while ((length = in.read(b)) != -1) {
            out.write(b, 0, length);
        }
    }
}
